package com.ideal.service.impl;

import com.ideal.constant.LocalPathConstant;
import com.ideal.constant.RegexConstant;
import com.ideal.service.RefactorMarkdownService;
import com.ideal.util.FileUtil;
import com.ideal.util.GetUrlImgUtil;
import com.ideal.util.SystemUtils;
import com.ideal.request.LocalPathRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <h1> 语雀导出格式修复 API 实现类 </h1>
 *
 * @author ideal-20
 * @date 2022-10-30 0:48
 **/
@Service
@Slf4j
public class RefactorMarkdownServiceImpl implements RefactorMarkdownService {
    /**
     * BR 处理次数（仅单线程串行可用）
     */
    private static int REFACTOR_BR_COUNT = 0;
    /**
     * 图片本地化处理次数（仅单线程串行可用）
     */
    private static int REFACTOR_IMG_TO_LOCAL_COUNT = 0;
    /**
     * 图片处理后缀次数（仅单线程串行可用）
     */
    private static int REFACTOR_IMG_SUFFIX_COUNT = 0;
    /**
     * LaTex 处理次数（仅单线程串行可用）
     */
    private static int REFACTOR_LATEX_COUNT = 0;
    /**
     * 处理文件总数（仅单线程串行可用）
     */
    private static int REFACTOR_FILE_COUNT = 0;

    /**
     * "# " 一级标题的序号
     */
    Integer FIRST_SHARP_COUNT = 1;

    @Override
    public boolean doRefactorLocal(LocalPathRequest localPath) {
        if (StringUtils.isBlank(localPath.getLocalPath()) || localPath.getPathType() == null) {
            return Boolean.FALSE;
        }
        // 开始处理
        if (LocalPathConstant.FILE_PATH.equals(localPath.getPathType())) {
            return refactorFilePath(localPath);
        }
        return refactorDirPath(localPath);
    }

    @Override
    public String doRefactorLocalForGui(LocalPathRequest localPath) {
        if (StringUtils.isBlank(localPath.getLocalPath()) || localPath.getPathType() == null) {
            return "";
        }
        long startTime = System.currentTimeMillis();
        // 开始处理
        if (LocalPathConstant.FILE_PATH.equals(localPath.getPathType())) {
            refactorFilePath(localPath);
            return printRefactorResultStr(startTime);
        }
        refactorDirPath(localPath);
        return printRefactorResultStr(startTime);
    }

    /**
     * <h2> 指定 Markdown 文件重构 </h2>
     */
    private boolean refactorFilePath(LocalPathRequest localPath) {
        long startTime = System.currentTimeMillis();
        String sourceDirPath = null;
        // 是否指定位置
        String targetDirPath = localPath.getTargetDirPath();
        if (!StringUtils.isBlank(targetDirPath)) {
            File sourceFile = new File(localPath.getLocalPath());
            sourceDirPath = sourceFile.getAbsoluteFile().toString().replaceAll(sourceFile.getName(), "");
            // 防止用户指定目录时不加最后一个斜杠
            if (SystemUtils.isWindows()) {
                if (!targetDirPath.endsWith("\\")) {
                    targetDirPath += "\\";
                }
            } else {
                if (!targetDirPath.endsWith("/")) {
                    targetDirPath += "/";
                }
            }
        }
        // 执行
        refactor(localPath.getLocalPath(), sourceDirPath, targetDirPath, localPath.getLocalImgRelativePath(), Boolean.FALSE);
        // 日志
        printRefactorResultLog(startTime);
        return Boolean.TRUE;
    }

    /**
     * <h2> 文件夹分层文件重构 </h2>
     */
    private boolean refactorDirPath(LocalPathRequest localPath) {
        long startTime = System.currentTimeMillis();
        // 读文件
        List<String> filePathList = new ArrayList<>();
        FileUtil.readFileInDir(localPath.getLocalPath(), localPath.getDirRecursive() != 0, filePathList);
        // 逐一执行
        for (String filePath : filePathList) {
            refactor(filePath,
                    localPath.getLocalPath(),
                    localPath.getTargetDirPath(),
                    localPath.getLocalImgRelativePath(),
                    Boolean.FALSE);
            REFACTOR_FILE_COUNT++;
        }
        printRefactorResultLog(startTime);
        return Boolean.TRUE;
    }

    private void printRefactorResultLog(long startTime) {
        log.info("\n" + "指定 Markdown 文件重构完成"
                        + "\n" + "处理换行:{}次"
                        + "\n" + "处理图片后缀:{}次"
                        + "\n" + "处理图片本地化:{}次"
                        + "\n" + "处理 LaTex:{}次"
                        + "\n" + "共 {} 个文件"
                        + "\n" + "总共耗时:{}ms",
                REFACTOR_BR_COUNT,
                REFACTOR_IMG_SUFFIX_COUNT,
                REFACTOR_IMG_TO_LOCAL_COUNT,
                REFACTOR_LATEX_COUNT,
                REFACTOR_FILE_COUNT,
                System.currentTimeMillis() - startTime);
    }

    private String printRefactorResultStr(long startTime) {
        return "\n" + "指定 Markdown 文件重构完成"
                + "\n" + "处理换行:" + REFACTOR_BR_COUNT + "次"
                + "\n" + "处理图片后缀:" + REFACTOR_IMG_SUFFIX_COUNT + "次"
                + "\n" + "处理图片本地化:" + REFACTOR_IMG_TO_LOCAL_COUNT + "次"
                + "\n" + "处理 LaTex:" + REFACTOR_LATEX_COUNT + "次"
                + "\n" + "共 " + REFACTOR_FILE_COUNT + " 个文件"
                + "\n" + "总共耗时:" + (System.currentTimeMillis() - startTime) + "ms";
    }

    /**
     * <h2> 重构 </h2>
     *
     * @param sourceFilePath       源文件绝对路径
     * @param sourceRootDirPath    源文件根路径（目录模式需要传递）
     * @param targetRootDirPath    源文件目标生成路径（目录模式需要传递）
     * @param localImgRelativePath 图片本地化相对路径 默认 images
     * @param repeatRefactor       再次处理（图片本地化等）
     */
    private void refactor(String sourceFilePath, String sourceRootDirPath, String targetRootDirPath, String localImgRelativePath, boolean repeatRefactor) {
        BufferedReader reader = null;
        BufferedWriter writer = null;
        String fileName = "";
        File sourceFile = new File(sourceFilePath);
        File tempFile = null;
        try {
            // 初始化
            reader = new BufferedReader(new InputStreamReader(Files.newInputStream(sourceFile.toPath()), StandardCharsets.UTF_8));
            // 只有没传递目标路径以及非本文件二次重构时才使用策略 1
            if (targetRootDirPath == null) {
                // 策略1: 覆盖源文件
                tempFile = new File(sourceFilePath + ".temp");
                writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(tempFile.toPath()), StandardCharsets.UTF_8));
            } else {
                // 策略2: 指定输出目录
                writer = refactorCreateTarget(sourceFilePath, sourceRootDirPath, targetRootDirPath);
            }
            fileName = sourceFile.getAbsolutePath();

            // 重构逻辑
            doLoopRefactor(reader, writer, repeatRefactor, sourceFile, sourceRootDirPath, targetRootDirPath, localImgRelativePath);
            Objects.requireNonNull(writer).flush();
        } catch (Exception e) {
            log.error("文件:{}, 重构异常，error:{}", fileName, e);
        } finally {
            // 关闭流
            try {
                Objects.requireNonNull(reader).close();
            } catch (IOException e) {
                log.error("文件:{}, 流关闭异常", fileName);
            }
            try {
                Objects.requireNonNull(writer).close();
            } catch (IOException e) {
                log.error("文件:{}, 流关闭异常", fileName);
            }

            // 覆盖写入删除源文件，将 temp 文件重命名为源文件
            if (targetRootDirPath == null && tempFile != null) {
                try {
                    boolean deleteRet = sourceFile.delete();
                    boolean renameRet = tempFile.renameTo(new File(tempFile.getPath().replaceAll(".temp", "")));
                    log.info("源文件删除 ret:{}, 临时文件替换为源文件 ret:{}", deleteRet, renameRet);
                } catch (Exception e) {
                    log.error("文件:{}, 临时文件文件替换失败，请检查源文件是否被占用", fileName);
                }
            }

            if (!repeatRefactor) {
                if (targetRootDirPath == null) {
                    log.info("开始执行单文件的二次重构");
                    refactor(sourceFilePath, sourceRootDirPath, null, localImgRelativePath, Boolean.TRUE);
                } else {
                    log.info("开始执行目录模式的文件二次重构");
                    String targetFilePath = sourceFilePath.replaceAll(sourceRootDirPath, targetRootDirPath);
                    refactor(targetFilePath, targetRootDirPath, null, localImgRelativePath, Boolean.TRUE);
                }
            }
        }
    }

    /**
     * <h2> 非覆盖重构 </h2>
     */
    private BufferedWriter refactorCreateTarget(String sourceFilePath, String sourceRootDirPath, String targetRootDirPath) throws IOException {
        // 获取新文件目录和文件名
        String targetDirPath = FileUtil.getTargetDirPathBySource(sourceFilePath, sourceRootDirPath, targetRootDirPath);
        String targetFileName = FileUtil.getTargetFileNamePathBySource(sourceFilePath, sourceRootDirPath, targetRootDirPath);

        log.info("文件开始输出到指定位置 目录:{} 文件名:{}", targetDirPath, targetFileName);

        // 先递归创建文件夹
        File dirFile = new File(targetDirPath);
        if (!dirFile.exists()) {
            boolean mkdirFlag = dirFile.mkdirs();
            log.info("文件夹:{} 创建结束 ret:{}", targetRootDirPath, mkdirFlag);
        }
        // 创建目标文件
        File targetFile = new File(targetDirPath, targetFileName);
        if (!targetFile.exists()) {
            try {
                boolean createFileFlag = targetFile.createNewFile();
                log.info("文件:{} 创建结束，rer={}", targetFile.getAbsolutePath(), createFileFlag);
            } catch (IOException e) {
                log.error("文件:{} 创建异常:{}", targetFile.getAbsolutePath(), e);
                return null;
            }
        }
        return new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(targetFile.toPath()), StandardCharsets.UTF_8));
    }

    /**
     * <h2> 逐行读取重构 </h2>
     */
    private void doLoopRefactor(BufferedReader reader, BufferedWriter writer, boolean repeatRefactor,
                                File sourceFile, String sourceRootDirPath, String targetRootDirPath, String localImgRelativePath) throws Exception {
        // 正则 pattern
        Pattern regexImgPattern = Pattern.compile(RegexConstant.REGEX_IMG_URL);
        Pattern excessSuffixPattern = Pattern.compile(RegexConstant.EXCESS_SUFFIX_REGEX);
        Pattern brTagPattern = Pattern.compile(RegexConstant.BR_TAG_REGEX);
        Pattern latexPattern = Pattern.compile(RegexConstant.LATEX_REGEX);

        String tempStr;
        while ((tempStr = reader.readLine()) != null) {
            // 避免格式错乱
            tempStr += "\n";
            if (repeatRefactor) {
                // 二次处理
                tempStr = processLineRepeat(regexImgPattern, latexPattern, tempStr, sourceFile, sourceRootDirPath, targetRootDirPath, localImgRelativePath);
            } else {
                // 通用处理
                tempStr = processLineCommon(excessSuffixPattern, brTagPattern, tempStr);
            }
            writer.write(tempStr);
        }
    }

    /**
     * <h2> 二次处理执行的逻辑 </h2>
     */
    private String processLineRepeat(Pattern regexImgPattern, Pattern latexPattern, String tempStr,
                                     File sourceFile, String sourceRootDirPath, String targetRootDirPath, String localImgRelativePath) throws Exception {
        // 图片本地化处理
        Matcher regexImgMatcher = regexImgPattern.matcher(tempStr);
        if (regexImgMatcher.find()) {
            String fullMatchStr = regexImgMatcher.group(0);
            // 选择图片写目录 (replace 的正则 对特殊字符会冲突)
            String absoluteFileStr = sourceFile.getAbsoluteFile().toString().replaceAll(sourceFile.getName(), "");
            String writePathCommon = absoluteFileStr.substring(0, absoluteFileStr.lastIndexOf(File.separator) + 1);

            tempStr = tempStr.replaceAll(RegexConstant.REGEX_IMG_URL,
                    GetUrlImgUtil.toLocal(fullMatchStr, writePathCommon, localImgRelativePath));
            REFACTOR_IMG_TO_LOCAL_COUNT++;
        }

        // LaTex 公式处理
        Matcher latexMatcher = latexPattern.matcher(tempStr);
        if (latexMatcher.find()) {
            String fullMatchStr = latexMatcher.group(0);
            String tempMatchStr = "";
            // 准备下数据
            String dollarStr = "$";
            String twoDollarStr = Matcher.quoteReplacement("$$");
            // 如果是 latex 的 $ 开头就直接处理，否则需要换行
            if (dollarStr.equals(tempStr.charAt(0) + "")) {
                tempMatchStr = fullMatchStr.replaceFirst(RegexConstant.DOLLAR_REGEX, twoDollarStr + "\n");
            } else {
                tempMatchStr = fullMatchStr.replaceFirst(RegexConstant.DOLLAR_REGEX, "\n\n" + twoDollarStr + "\n");
            }
            // 如果是 latex 的 $ 结尾就直接处理，否则需要换行
            String s = tempStr.charAt(tempStr.length() - 1) + "";
            if (dollarStr.equals(s)) {
                tempMatchStr = tempMatchStr.replaceAll(RegexConstant.END_WITH_DOLLAR_REGEX, "\n" + twoDollarStr);
            } else {
                tempMatchStr = tempMatchStr.replaceAll(RegexConstant.END_WITH_DOLLAR_REGEX, "\n" + twoDollarStr + "\n");
            }
            tempStr = tempStr.replaceAll(RegexConstant.LATEX_REGEX, Matcher.quoteReplacement(tempMatchStr));
            REFACTOR_LATEX_COUNT++;
        }
        return tempStr;
    }

    /**
     * <h2> 首次处理执行的逻辑 </h2>
     */
    private String processLineCommon(Pattern excessSuffixPattern, Pattern brTagPattern, String tempStr) {
        /*
            语雀自动编号 导出不显示一级标题，因为 1. 1.1. 都被认为自动编号了
            Eg: 一级标题（一级标题需要添加 # 号）
                1.1 二级标题
                1.2 三级标题

                参考代码：已废弃
                if (StringUtils.isNotBlank(tempStr) && tempStr.length() >= 3) {
                    char firstSharpNextChar = tempStr.charAt(2);
                    if (tempStr.startsWith("# ")  && !Character.isDigit(firstSharpNextChar)) {
                        tempStr = tempStr.replaceAll("# ", "# " + FIRST_SHARP_COUNT + ". ");
                        FIRST_SHARP_COUNT++;
                    }
                }
         */

        // 多余后缀处理
        Matcher excessSuffixMatcher = excessSuffixPattern.matcher(tempStr);
        if (excessSuffixMatcher.find()) {
            tempStr = tempStr.replaceAll(RegexConstant.EXCESS_SUFFIX_REGEX, "");
            REFACTOR_IMG_SUFFIX_COUNT++;
        }

        // 段间距处理
        Matcher brTagMatcher = brTagPattern.matcher(tempStr);
        if (brTagMatcher.find()) {
            tempStr = tempStr.replaceAll(RegexConstant.BR_TAG_REGEX, "" + "\n\n");
            REFACTOR_BR_COUNT++;
        }
        return tempStr;
    }
}